package bigoo.peer;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class RideManager {

    private static int RIDES_TO_PROPAGATE = 5;

    private final Map<String, Ride> availableRides = new HashMap<String, Ride>(); // peerIDrideID
    // x
    // ride
    private final Lock monitor = new ReentrantLock();

    public Ride getRide(String rideID) {
	this.monitor.lock();
	Ride result = this.availableRides.get(rideID);
	this.monitor.unlock();
	return result;
    }

    public Collection<Ride> getSomeRides(String toPeerId) {
	List<Ride> exclusive = new LinkedList<Ride>();
	this.monitor.lock();
	for (Ride ride : this.availableRides.values()) {
	    if (!ride.getPeerID().equals(toPeerId)) {
		exclusive.add(ride);
	    }
	}
	this.monitor.unlock();

	int size = exclusive.size();
	if (size > RIDES_TO_PROPAGATE) {
	    Collection<Ride> result = new LinkedList<Ride>();
	    for (int i = 0; i < RIDES_TO_PROPAGATE; i++) {
		result.add(exclusive.remove((int) Math.round(Math.random() * (size - 1))));
		size--;
	    }
	    return result;
	}

	return exclusive;
    }

    public String registerRide(String peerID, int destination, int places, int date) {
	this.monitor.lock();
	try {
	    String rideID = this.generateID();
	    while (this.availableRides.containsKey(rideID)) {
		rideID = this.generateID();
	    }
	    Ride r = new Ride(destination, rideID, peerID, places, date, 0);
	    this.availableRides.put(r.getRideID(), r);
	    return rideID;
	} finally {
	    this.monitor.unlock();
	}
    }

    /**
     * Merge the given rides with the current rides.
     * 
     * @param rides
     *            The new rides to be merged
     * @return the number of new rides merged to current rides.
     */
    public int mergeRides(Collection<Ride> rides) {
	int count = 0;
	this.monitor.lock();
	for (Ride ride : rides) {
	    String rideID = ride.getRideID();
	    if (this.availableRides.containsKey(rideID)) {
		Ride myRide = this.availableRides.get(rideID);
		if (ride.getVersion() > myRide.getVersion()) {
		    this.availableRides.put(rideID, ride);
		    count++;
		}
	    } else {
		this.availableRides.put(rideID, ride);
		count++;
	    }
	}
	this.monitor.unlock();
	return count;

    }

    public Collection<Ride> getAvailableRiders() {
	this.monitor.lock();
	try {
	    return this.availableRides.values();
	} finally {
	    this.monitor.unlock();
	}
    }

    private String generateID() {
	return String.valueOf(System.nanoTime());
    }

}
